Lightkurve quicklook prototype

In [1]:
%matplotlib notebook
from lightkurve import KeplerTargetPixelFile
import numpy as np

Let’s make the Jupyter cells wider

In [2]:
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))

Download a Kepler target pixel file (TPF)

In [6]:
tpf = KeplerTargetPixelFile.from_archive('210698281', campaign=13)
In [7]:
xx=tpf.column + np.arange(tpf.shape[2])
yy=tpf.row + np.arange(tpf.shape[1])

x, y = np.meshgrid(xx, yy)

x_vals, y_vals = x[tpf.pipeline_mask], y[tpf.pipeline_mask]

x_vals, y_vals
Out[7]:
(array([491, 492, 489, 490, 491, 492, 493, 489, 490, 491, 492, 493, 491,
        492, 493, 491, 492, 493]),
 array([469, 469, 470, 470, 470, 470, 470, 471, 471, 471, 471, 471, 472,
        472, 472, 473, 473, 473]))

Define a new interact routine including pixel selection for the mask

In [15]:
lc=tpf.to_lightcurve()

"""
Interact with a linked target pixel file and lightcurve

Parameters
----------
lc : an optional user-supplied pre-processed lightcurve for this target

Returns
-------
ax : matplotlib.axes._subplots.AxesSubplot
    The matplotlib axes object.
"""
try:
    from ipywidgets import interact
    import ipywidgets as widgets
    from bokeh.io import push_notebook, show, output_notebook
    from bokeh.plotting import figure, ColumnDataSource
    from bokeh.models import Span
    from bokeh.models import LogColorMapper, CustomJS, Slider
    from bokeh.layouts import row, column
    from bokeh.themes import Theme
    from bokeh.models.tools import HoverTool, PointDrawTool
    from bokeh.models.widgets import Button
    output_notebook()
except ImportError:
    raise ImportError('The quicklook tool requires Bokeh and ipywidgets.  See the Installation Guide.')

if lc is None:
    lc = tpf.to_lightcurve()

source = ColumnDataSource(data=dict(
    time=lc.time, flux=lc.flux,
    cadence=lc.cadenceno,
    quality=lc.quality))

source3 = ColumnDataSource(data=dict(tpf_flux=tpf.flux))

title = "Quicklook lightcurve for {} target {}".format(tpf.mission, tpf.keplerid)
p = figure(title=title, plot_height=300, plot_width=600, tools="tap,pan,wheel_zoom,box_zoom,reset")#, theme=theme)
p.yaxis.axis_label = 'Normalized Flux'
p.xaxis.axis_label = 'Time - 2454833 (days)'
p.step('time', 'flux', line_width=1, color='gray', source=source, nonselection_line_color='gray')

r = p.circle('time', 'flux', source=source, fill_alpha=0.3, size=8,line_color=None,
             selection_color="firebrick", nonselection_fill_alpha=0.0,
             nonselection_fill_color="grey",nonselection_line_color=None,
             nonselection_line_alpha=0.0, fill_color=None,
             hover_fill_color="firebrick",hover_alpha=0.9,hover_line_color="white")

p.add_tools(HoverTool(tooltips=[("index", "$index"),
                                ("cadence", "@cadence"),
                                ("time", "@time{0,0.000}"),
                                ("flux", "@flux"),
                                ("quality", "@quality")],
                      renderers=[r], mode='mouse', point_policy="snap_to_data"))

vert = Span(location=800, dimension='height', line_color='firebrick', line_width=4, line_alpha=0.5)
p.add_layout(vert)
s2 = figure(plot_width=300, plot_height=300, title='Target Pixel File', tools='tap, box_zoom, reset')
s2.yaxis.axis_label = 'Pixel Row Number'
s2.xaxis.axis_label = 'Pixel Column Number'

pedestal = np.nanmin(tpf.flux)
vlo, lo, med, hi, vhi = np.fix(np.nanpercentile(tpf.flux-pedestal, [0.2, 1, 50, 95, 99.8]))
color_mapper = LogColorMapper(palette="Viridis256", low=lo, high=hi)

s2_dat = s2.image([pedestal+tpf.flux[0,:,:]], x=tpf.column, y=tpf.row,
                  dw=tpf.shape[2], dh=tpf.shape[1], dilate=True,
                  color_mapper=color_mapper)

source2 = ColumnDataSource(data=dict(xx=x_vals+0.5, yy=y_vals+0.5))
r1 = s2.rect('xx', 'yy', 1, 1, source=source2, fill_color='gray', fill_alpha=0.4, line_color='white')
ptool = PointDrawTool(renderers=[r1])
s2.add_tools(ptool)
s2.toolbar.active_tap = ptool


def my_text_input_handler(attr, old, new):
    print("X")
    #uopdated label: " + new)


#ptool.on_change("value", my_text_input_handler)


#button = Button(label="Foo", button_type="success")
#def my_button_handler(new):
#    print('Button option ' + str(new) + ' selected.')

#button.on_click(my_button_handler)


def update(f, v):
    vert.update(location=tpf.time[f])
    s2_dat.data_source.data['image'] = [tpf.flux[f,:,:]]
    s2_dat.glyph.color_mapper.high = v[1]
    s2_dat.glyph.color_mapper.low = v[0]
    push_notebook()


callback = CustomJS(args=dict(source=source, source2=source2, source3=source3), code="""
    var data = source3.data['tpf_flux'];
    var lcurve = source.data['flux']
    var f = cb_obj.value
    for (var i = 0; i < lcurve.length; i++) {
        lcurve[i] = lcurve[i] - f
    }
    source.change.emit();
""")

slider = Slider(start=0.1, end=4, value=1, step=.1, title="power")
slider.js_on_change('value', callback)

row1 = row(p, s2)
row_and_col = column(slider, row1)

show(row_and_col, notebook_handle=True)
n_cad, nx, ny = tpf.flux.shape

play = widgets.Play(
    interval=10,
    value=9,
    min=0,
    max=n_cad-1,
    step=1,
    description="Press play",
    disabled=False)


wbutton = widgets.Button(
    description='Click me',
    disabled=False,
    button_style='', # 'success', 'info', 'warning', 'danger' or ''
    icon='check')

def on_button_clicked(b):
    bool_mask = tpf.pipeline_mask*False
    for xi, yi in zip(r1.data_source.data['xx'], r1.data_source.data['yy']):
        xc = np.int(np.round(xi-0.5)-tpf.column)
        yc = np.int(np.round(yi-0.5)-tpf.row)
        bool_mask[xc, yc] = True
        print(xi, yi, xc, yc)
    source.data['flux'] = tpf.to_lightcurve(aperture_mask=bool_mask).flux
    print('click', bool_mask.sum())

    print('glyph', vars(r1.glyph))
    print('data_source', vars(r1.data_source))
    print('selected', vars(r1.data_source.selected))

wbutton.on_click(on_button_clicked)

f_slider = widgets.IntSlider(min=0,max=n_cad-1,step=1,value=5,
                             layout=widgets.Layout(width='40%', height='20px'))

vstep = np.round((hi-lo)/300.0, 1)

v_slider = widgets.FloatRangeSlider(value=[lo, hi],
                                    min=0,
                                    max=hi,
                                    step=vstep,
                                    description='v:',
                                    continuous_update=False,
                                    layout=widgets.Layout(width='30%', height='20px'))

widgets.jslink((play, 'value'), (f_slider, 'value'))
ui = widgets.HBox([play, f_slider, v_slider, wbutton])
out = widgets.interactive_output(update, {'f': f_slider, 'v':v_slider})
display(ui, out)

#return p

xx=tpf.column + np.arange(tpf.shape[2])
yy=tpf.row + np.arange(tpf.shape[1])
x, y = np.meshgrid(xx, yy)
x_vals, y_vals = x[tpf.pipeline_mask], y[tpf.pipeline_mask]
x_vals = x_vals[1:2]
y_vals = y_vals[1:2]
print(x_vals)
print(y_vals)
p
Loading BokehJS ...
[492]
[469]
Out[15]:
Figure(
id = '3a9f33c6-b3c7-45a7-973a-b1d4d9faf80a', …)

Hmmm… it’s not working!